using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PACKETPARSERMAX20.CORE;

namespace PACKETPARSERMAX20.CORE.DEFINITIONS
{
    public class SMSG_SPELL_START_DEF : DefinitionBase
    {

        public override bool Process()
        {
            ResetPosition();

            SpellStart spellstart = new SpellStart(this);


            spellstart.castitem_or_caster = popGuid();
            spellstart.caster = popGuid();


            if (ClientBuild >= 9183)
            {
                spellstart.cast_count = ReadByte();                             // pending spell cast?
                spellstart.spellInfoId = ReadUInt32();                        // spellId
                spellstart.castflags = (SpellCastFlags)ReadUInt32();
            }
            else
            {
                spellstart.spellInfoId = ReadUInt32();
                spellstart.cast_count = ReadByte();                           // single cast or multi 2.3 (0/1)
                spellstart.castflags = (SpellCastFlags)ReadUInt16();
            }

            spellstart.getMSTime = ReadUInt32();                          // timestamp

            if (ClientBuild <= 7799)
            {
                spellstart.targetMask = (SpellCastTargetFlags)ReadUInt16();
                //spellstart.targetMask.DumpFlags();

                if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_UNIT | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE | SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_UNK2)) > 0)
                {
                    if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_UNIT) > 0)
                    {
                        spellstart.unitTarget = popGuid();
                    }
                    else if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_OBJECT_UNK)) > 0)
                    {
                        spellstart.GOTarget = popGuid();
                    }
                    else if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE)) > 0)
                    {
                        spellstart.CorpseTarget = popGuid();
                    }
                    else
                    {
                        spellstart.otherTarget = popGuid();
                    }
                }

                if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_ITEM | SpellCastTargetFlags.TARGET_FLAG_TRADE_ITEM)) > 0)
                {
                    spellstart.itemTarget = popGuid();
                }

                if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_SOURCE_LOCATION) > 0)
                {
                    spellstart.srcX = ReadSingle();
                    spellstart.srcY = ReadSingle();
                    spellstart.srcZ = ReadSingle();
                }

                if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_DEST_LOCATION) > 0)
                {
                    spellstart.destX = ReadSingle();
                    spellstart.destY = ReadSingle();
                    spellstart.destZ = ReadSingle();
                }

                if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_STRING) > 0)
                {
                    spellstart.strTarget = ReadString();
                }

                if ((spellstart.castflags & SpellCastFlags.CAST_FLAG_AMMO) > 0)
                {
                    spellstart.ammoDisplayID = ReadUInt32();
                    spellstart.ammoInventoryType = ReadUInt32();

                }
            }
            else
            {
                spellstart.targetMask = (SpellCastTargetFlags)ReadUInt32();
                //spellstart.targetMask.DumpFlags();

                if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_UNIT | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE | SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_UNK2)) > 0)
                {
                    if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_UNIT) > 0)
                    {
                        spellstart.unitTarget = popGuid();
                    }
                    else if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_OBJECT_UNK)) > 0)
                    {
                        spellstart.GOTarget = popGuid();
                    }
                    else if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE)) > 0)
                    {
                        spellstart.CorpseTarget = popGuid();
                    }
                    else
                    {
                        spellstart.otherTarget = popGuid();
                    }
                }

                if ((spellstart.targetMask & (SpellCastTargetFlags.TARGET_FLAG_ITEM | SpellCastTargetFlags.TARGET_FLAG_TRADE_ITEM)) > 0)
                {
                    spellstart.itemTarget = popGuid();
                }

                if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_SOURCE_LOCATION) > 0)
                {
                    if (ClientBuild >= 10314)
                    {
                        spellstart.unksrcbyte = ReadByte();
                    }
                    spellstart.srcX = ReadSingle();
                    spellstart.srcY = ReadSingle();
                    spellstart.srcZ = ReadSingle();
                }

                if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_DEST_LOCATION) > 0)
                {
                    if (ClientBuild >= 9551)
                    {
                        spellstart.unkdestbyte = ReadByte();
                    }

                    spellstart.destX = ReadSingle();
                    spellstart.destY = ReadSingle();
                    spellstart.destZ = ReadSingle();
                }

                if ((spellstart.targetMask & SpellCastTargetFlags.TARGET_FLAG_STRING) > 0)
                {
                    spellstart.strTarget = ReadString();
                }

                if (ClientBuild >= 9183)
                {
                    if ((spellstart.castflags & SpellCastFlags.CAST_FLAG_UNKNOWN6) > 0)                  // predicted power?
                    {
                        spellstart.predicted_power = ReadUInt32();
                    }

                    if ((spellstart.castflags & SpellCastFlags.CAST_FLAG_UNKNOWN7) > 0)                   // rune cooldowns list
                    {
                        spellstart.runes_state_before = ReadByte();                                  // runes state before
                        spellstart.runes_state_after = ReadByte();                                 // runes state after
                        spellstart.runes_state_unk_list = new SortedList<int, byte>();

                        for (byte i = 0; i < 6; ++i)
                        {
                            byte m = (byte)(1 << (int)i);
                            if ((m & spellstart.runes_state_before) > 0)
                            {
                                // usable before...
                                if (!((m & spellstart.runes_state_after) > 0))                               // ...but on cooldown now...
                                {
                                    byte unk = ReadByte();
                                    spellstart.Add_runes_state_unk(i, unk);                       // some unknown byte (time?)
                                }
                            }
                        }
                    }

                }

                if ((spellstart.castflags & SpellCastFlags.CAST_FLAG_AMMO) > 0)
                {
                    spellstart.ammoDisplayID = ReadUInt32();
                    spellstart.ammoInventoryType = ReadUInt32();
                }

                if (ClientBuild >= 10482)
                {
                    if ((spellstart.castflags & SpellCastFlags.CAST_FLAG_UNKNOWN21) > 0)
                    {
                        spellstart.CAST_FLAG_UNKNOWN21_uint_1 = ReadUInt32();
                        spellstart.CAST_FLAG_UNKNOWN21_uint_2 = ReadUInt32();
                    }
                }
            }

            return Validate();
        }

        public bool Process_BackUp()
        {
            ResetPosition();

           // InitLog();

            UInt64 m_castitem_or_caster = popGuid();
            UInt64 m_caster = popGuid();

            //if (LogIt) LogAppendLine("UInt64", "m_castitem_or_caster", m_castitem_or_caster, true);
            //if (LogIt) LogAppendLine("UInt64", "m_caster", m_caster, true);

            SpellCastFlags castFlags;
            byte m_cast_count = 0;
            UInt32 m_spellInfoId = 0;

            if (ClientBuild >= 9183)
            {
                m_cast_count = ReadByte();                             // pending spell cast?
                m_spellInfoId = ReadUInt32();                        // spellId
                castFlags = (SpellCastFlags)ReadUInt32();
                //castFlags.DumpFlags();

                //if (LogIt) LogAppendLine("byte", "m_cast_count", m_cast_count);
                //if (LogIt) LogAppendLine("byte", "m_spellInfoId", m_spellInfoId);
                //if (LogIt) LogAppendLine("UInt32", "castFlags", (UInt32)castFlags, "", castFlags.DumpFlags());
            }
            else
            {
                m_spellInfoId = ReadUInt32();
                m_cast_count = ReadByte();                           // single cast or multi 2.3 (0/1)
                castFlags = (SpellCastFlags)ReadUInt16();


                //if (LogIt) LogAppendLine("byte", "m_cast_count", m_cast_count);
                //if (LogIt) LogAppendLine("byte", "m_spellInfoId", m_spellInfoId);
                //if (LogIt) LogAppendLine("UInt16", "castFlags", (UInt16)castFlags, "", castFlags.DumpFlags());
                //LogAppend(castFlags.DumpFlags());
            }


            //if (StaticObjects.knockbackspells.Contains(m_spellInfoId)    )
            //{
            //    if (m_caster.ToString()[0] == '4' || m_castitem_or_caster.ToString()[0] == '4')
            //    {

            //        System.IO.StreamWriter sww = new System.IO.StreamWriter("found.log", true);
            //        sww.WriteLine(string.Format("{0}\t{1}\t{2}", m_spellInfoId,base.context.Reader.CurrentIndex, context.Reader.FileName));
            //        sww.Close();
            //    }
            //}

            //if (!StaticObjects.m_spellInfoIdList.Contains(m_spellInfoId))
            //{
            //    StaticObjects.m_spellInfoIdList.Add(m_spellInfoId);
            //    System.IO.StreamWriter sw = new System.IO.StreamWriter("m_spellInfoIdList.log", true);
            //    sw.WriteLine(m_spellInfoId);
            //    sw.Close();
            //}
            UInt32 getMSTime = ReadUInt32();                          // timestamp
            //if (LogIt) LogAppendLine("UInt32", "getMSTime", getMSTime);

            if (ClientBuild <= 7799)
            {
                SpellCastTargetFlags m_targetMask = (SpellCastTargetFlags)ReadUInt16();
                //m_targetMask.DumpFlags();

                if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_UNIT | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE | SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_UNK2)) > 0)
                {
                    if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_UNIT) > 0)
                    {
                        ulong m_unitTarget = popGuid();
                    }
                    else if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_OBJECT_UNK)) > 0)
                    {
                        ulong m_GOTarget = popGuid();
                    }
                    else if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE)) > 0)
                    {
                        ulong m_CorpseTargetGUID = popGuid();
                    }
                    else
                    {
                        ulong none = popGuid();
                    }
                }

                if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_ITEM | SpellCastTargetFlags.TARGET_FLAG_TRADE_ITEM)) > 0)
                {
                    ulong m_itemTarget = popGuid();
                }

                if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_SOURCE_LOCATION) > 0)
                {
                    float m_srcX = ReadSingle();
                    float m_srcY = ReadSingle();
                    float m_srcZ = ReadSingle();
                }

                if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_DEST_LOCATION) > 0)
                {
                    float m_destX = ReadSingle();
                    float m_destY = ReadSingle();
                    float m_destZ = ReadSingle();
                }

                if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_STRING) > 0)
                {
                    string m_strTarget = ReadString();
                }

                if ((castFlags & SpellCastFlags.CAST_FLAG_AMMO) > 0)
                {
                    UInt32 ammoDisplayID = ReadUInt32();
                    UInt32 ammoInventoryType = ReadUInt32();

                }
            }
            else
            {
                SpellCastTargetFlags m_targetMask = (SpellCastTargetFlags)ReadUInt32();
                //if (LogIt) //if (LogIt) LogAppendLine("UInt32", "m_targetMask", (UInt32)m_targetMask, "", m_targetMask.DumpFlags());

                //m_targetMask.DumpFlags();

                if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_UNIT | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE | SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_UNK2)) > 0)
                {
                    if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_UNIT) > 0)
                    {
                        ulong m_unitTarget = popGuid();
                        //if (LogIt) LogAppendLine("UInt64", "m_unitTarget", m_unitTarget, true);

                    }
                    else if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_OBJECT | SpellCastTargetFlags.TARGET_FLAG_OBJECT_UNK)) > 0)
                    {
                        ulong m_GOTarget = popGuid();
                        //if (LogIt) LogAppendLine("UInt64", "m_GOTarget", m_GOTarget, "TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK", true);
                    }
                    else if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_CORPSE | SpellCastTargetFlags.TARGET_FLAG_PVP_CORPSE)) > 0)
                    {
                        ulong m_CorpseTargetGUID = popGuid();
                        //if (LogIt) LogAppendLine("UInt64", "m_CorpseTargetGUID", m_CorpseTargetGUID, "TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE", true);

                    }
                    else
                    {
                        ulong otherguid = popGuid();
                        //if (LogIt) LogAppendLine("UInt64", "otherguid", otherguid, true);

                    }
                }

                if ((m_targetMask & (SpellCastTargetFlags.TARGET_FLAG_ITEM | SpellCastTargetFlags.TARGET_FLAG_TRADE_ITEM)) > 0)
                {
                    ulong m_itemTarget = popGuid();
                    //if (LogIt) LogAppendLine("UInt64", "m_itemTarget", m_itemTarget, "TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM", true);

                }

                if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_SOURCE_LOCATION) > 0)
                {
                    if (ClientBuild >= 10314)
                    {
                        byte unkb = ReadByte();
                        //if (LogIt) LogAppendLine("byte", "unkb", unkb, "TARGET_FLAG_DEST_LOCATION");
                    }
                    float m_srcX = ReadSingle();
                    float m_srcY = ReadSingle();
                    float m_srcZ = ReadSingle();


                    //if (LogIt) LogAppendLine("float", "m_srcX", m_srcX, "TARGET_FLAG_SOURCE_LOCATION");
                    //if (LogIt) LogAppendLine("float", "m_srcY", m_srcY, "TARGET_FLAG_SOURCE_LOCATION");
                    //if (LogIt) LogAppendLine("float", "m_srcZ", m_srcZ, "TARGET_FLAG_SOURCE_LOCATION");
                }

                if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_DEST_LOCATION) > 0)
                {
                    if (ClientBuild >= 9551)
                    {
                        byte unkb = ReadByte();
                        //if (LogIt) LogAppendLine("byte", "unkb", unkb, "TARGET_FLAG_DEST_LOCATION");
                    }

                    float m_destX = ReadSingle();
                    float m_destY = ReadSingle();
                    float m_destZ = ReadSingle();

                    //if (LogIt) LogAppendLine("float", "m_destX", m_destX, "TARGET_FLAG_DEST_LOCATION");
                    //if (LogIt) LogAppendLine("float", "m_destY", m_destY, "TARGET_FLAG_DEST_LOCATION");
                    //if (LogIt) LogAppendLine("float", "m_destZ", m_destZ, "TARGET_FLAG_DEST_LOCATION");


                }

                if ((m_targetMask & SpellCastTargetFlags.TARGET_FLAG_STRING) > 0)
                {
                    string m_strTarget = ReadString();
                    //if (LogIt) LogAppendLine("string", "m_strTarget", m_strTarget, "TARGET_FLAG_STRING");
                }

                if (ClientBuild >= 9183)
                {
                    if ((castFlags & SpellCastFlags.CAST_FLAG_UNKNOWN6) > 0)                  // predicted power?
                    {
                        UInt32 predicted_power = ReadUInt32();
                        //if (LogIt) LogAppendLine("UInt32", "predicted_power", predicted_power, "CAST_FLAG_UNKNOWN6");
                    }
                    if ((castFlags & SpellCastFlags.CAST_FLAG_UNKNOWN7) > 0)                   // rune cooldowns list
                    {
                        byte runes_state_before = ReadByte();                                  // runes state before
                        byte runes_state_after = ReadByte();                                 // runes state after

                        //if (LogIt) LogAppendLine("byte", "runes_state_before", runes_state_before, "CAST_FLAG_UNKNOWN7");
                        //if (LogIt) LogAppendLine("byte", "runes_state_after", runes_state_after, "CAST_FLAG_UNKNOWN7");

                        for (byte i = 0; i < 6; ++i)
                        {
                            byte m = (byte)(1 << (int)i);
                            if ((m & runes_state_before) > 0)
                            {
                                // usable before...
                                if (!((m & runes_state_after) > 0))                               // ...but on cooldown now...
                                {
                                    byte unk = ReadByte();                       // some unknown byte (time?)
                                    //if (LogIt) LogAppendLine("byte", "unk", unk, i);
                                }
                            }
                        }
                    }

                }

                if ((castFlags & SpellCastFlags.CAST_FLAG_AMMO) > 0)
                {
                    UInt32 ammoDisplayID = ReadUInt32();
                    UInt32 ammoInventoryType = ReadUInt32();
                    //if (LogIt) LogAppendLine("UInt32", "ammoDisplayID", ammoDisplayID, "CAST_FLAG_AMMO");
                    //if (LogIt) LogAppendLine("UInt32", "ammoInventoryType", ammoInventoryType, "CAST_FLAG_AMMO");
                }

                if (ClientBuild >= 10482)
                {
                    if ((castFlags & SpellCastFlags.CAST_FLAG_UNKNOWN21) > 0)
                    {
                        var unk1 = ReadUInt32();
                        var unk2 = ReadUInt32();
                    }
                }
            }

            return Validate();
        }

    }
}
